/*
 * @Author: 0x9DEFA478
 * @Date: 2025-10-04 18:19:11
 * @LastEditTime: 2025-10-05 18:38:03
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * 
 */
#include "./Device_HID.hpp"
#if defined(_WIN32)
#include "../../Utility/H_String.hpp"
#include "./IO_Overlapped.hpp"
#include "./DeviceEnum.hpp"
#include "./Encoding.hpp"
#include "./Error.hpp"
#include <windows.h>
extern "C"{
  #include <hidsdi.h>
}
#include <stdexcept>
#endif




namespace SystemAPI{

  namespace Windows{

#if defined(_WIN32)
    namespace{
      struct Device_HID_Data{
        IO_Overlapped io_overlapped;
        int InputReportSize;
        int OutputReportSize;

        static constexpr Device_HID_Data* Cast(void* pdata){return static_cast<Device_HID_Data*>(pdata);}
      };
    }

    Device_HID::Device_HID():privateData(nullptr){}

    Device_HID::Device_HID(const std::string& Path):path(Path),privateData(nullptr){}

    Device_HID::~Device_HID()noexcept{
      if(privateData!=nullptr){
        delete Device_HID_Data::Cast(privateData);
      }
    }

    Device_HID::Device_HID(Device_HID&& device_hid)noexcept:path(std::move(device_hid.path)),privateData(device_hid.privateData){
      device_hid.privateData=nullptr;
    }

    Device_HID& Device_HID::operator=(Device_HID&& device_hid)noexcept{
      if(privateData!=nullptr){
        delete Device_HID_Data::Cast(privateData);
      }
      path=std::move(device_hid.path);
      privateData=device_hid.privateData;
      device_hid.privateData=nullptr;
      return *this;
    }

    int Device_HID::InputReportSize()const noexcept{
      if(privateData==nullptr){
        return -1;
      }
      return Device_HID_Data::Cast(privateData)->InputReportSize;
    }

    int Device_HID::OutputReportSize()const noexcept{
      if(privateData==nullptr){
        return -1;
      }
      return Device_HID_Data::Cast(privateData)->OutputReportSize;
    }

    void Device_HID::Open(bool InfoMode){
      using namespace SystemAPI;

      if(privateData!=nullptr){
        return;
      }

      auto wpath=Windows::TextConvertToWideString(CP_UTF8,path);
      DWORD dwDesiredAccess=GENERIC_READ|GENERIC_WRITE;
      DWORD dwShareMode=0;
      if(InfoMode){
        dwDesiredAccess=0;
        dwShareMode=FILE_SHARE_READ|FILE_SHARE_WRITE;
      }
      auto handle=Windows::Handle(CreateFileW(&wpath[0],dwDesiredAccess,dwShareMode,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL));

      if(handle.GetHandle()==INVALID_HANDLE_VALUE){
        throw std::runtime_error("\""+path+"\" 打开失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }

      auto pdata=new Device_HID_Data;
      pdata->io_overlapped=IO_Overlapped(std::move(handle));

      PHIDP_PREPARSED_DATA preparsedData;
      HIDP_CAPS capabilities;

      if(FALSE==HidD_GetPreparsedData(pdata->io_overlapped.GetHandle(),&preparsedData)){
        throw std::runtime_error("\""+path+"\" HidD_GetPreparsedData失败: "+Windows::GetErrorStringWithErrorCode(GetLastError()));
      }
      NTSTATUS status;
      if(HIDP_STATUS_SUCCESS!=(status=HidP_GetCaps(preparsedData,&capabilities))){
        throw std::runtime_error("\""+path+"\" HidP_GetCaps失败: "+H::String::Format("%08lX",status));
      }

      pdata->InputReportSize=capabilities.InputReportByteLength;
      pdata->OutputReportSize=capabilities.OutputReportByteLength;

      privateData=static_cast<void*>(pdata);
      
      InfoUpdate();
    }

    void Device_HID::Close()noexcept{
      if(privateData!=nullptr){
        delete Device_HID_Data::Cast(privateData);
        privateData=nullptr;
      }
    }

    int Device_HID::Read(void* Buffer,int BufferSize,int Timeout){
      if(privateData==nullptr){
        return 0;
      }

      auto pdata=Device_HID_Data::Cast(privateData);
      
      pdata->io_overlapped.ReadFile(Buffer,BufferSize);
      auto res=pdata->io_overlapped.WaitRead(Timeout);
      if(res==-1){
        pdata->io_overlapped.CancelRead();
        auto res_timeout=pdata->io_overlapped.WaitRead(Timeout);
        if(res_timeout!=-2){
          res=res_timeout;
        }
      }

      return res;
    }

    int Device_HID::Write(const void* Data,int DataLength,int Timeout){
      if(privateData==nullptr){
        return 0;
      }

      auto pdata=Device_HID_Data::Cast(privateData);
      
      pdata->io_overlapped.WriteFile(Data,DataLength);
      auto res=pdata->io_overlapped.WaitWrite(Timeout);
      if(res==-1){
        pdata->io_overlapped.CancelWrite();
        auto res_timeout=pdata->io_overlapped.WaitWrite(Timeout);
        if(res_timeout!=-2){
          res=res_timeout;
        }
      }

      return res;
    }

    std::list<std::string> Device_HID::FindDeviceList(){
      GUID guid;
      HidD_GetHidGuid(&guid);
      return SystemAPI::Windows::EnumDevice_ByGUID(&guid);
    }

    void Device_HID::InfoUpdate(){
      if(privateData==nullptr){
        return;
      }

      auto pdata=Device_HID_Data::Cast(privateData);
      auto buffer=H::Pointer<WCHAR>::Array(2048);

      if(FALSE==HidD_GetProductString(pdata->io_overlapped.GetHandle(),&buffer[0],4093)){
        productString="";
      }else{
        productString=Windows::TextConvertFromWideString(CP_UTF8,&buffer[0]);
      }

    }

#endif

  }
}



